#region Copyright(c) Anton Shelin, Vladimir Timashkov. All Rights Reserved.
// -----------------------------------------------------------------------------
// Copyright(c) 2010 Anton Shelin, Vladimir Timashkov. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   1. No Trademark License - Microsoft Public License (Ms-PL) does not grant you rights to use
//      authors names, logos, or trademarks.
//   2. If you distribute any portion of the software, you must retain all copyright,
//      patent, trademark, and attribution notices that are present in the software.
//   3. If you distribute any portion of the software in source code form, you may do
//      so only under this license by including a complete copy of Microsoft Public License (Ms-PL)
//      with your distribution. If you distribute any portion of the software in compiled
//      or object code form, you may only do so under a license that complies with
//      Microsoft Public License (Ms-PL).
//   4. The names of the authors may not be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// The software is licensed "as-is." You bear the risk of using it. The authors
// give no express warranties, guarantees or conditions. You may have additional consumer
// rights under your local laws which this license cannot change. To the extent permitted
// under your local laws, the authors exclude the implied warranties of merchantability,
// fitness for a particular purpose and non-infringement.
// -----------------------------------------------------------------------------
#endregion

using System;
using System.IO;

namespace SharpDom.Attributes
{
    /// <summary>Value of attribute</summary>
    public class AttributeValue<TBaseValue>
    {
        /// <summary>Closed constructor</summary>
        protected AttributeValue() { }

        /// <summary>Value of the attribute</summary>
        public TBaseValue BaseValue { get; internal set; }

        /// <summary>Shows the content of the attribute value</summary>
        /// <returns>String representing the attribute value</returns>
        public override string ToString()
        {
            return BaseValue.ToString();
        }

        /// <summary>Conversion from base type of attribute value into attribute value instance</summary>
        /// <param name="value">Base type of attribute value</param>
        /// <returns>Attribute value instance</returns>
        public static implicit operator AttributeValue<TBaseValue>(TBaseValue value)
        {
            return new AttributeValue<TBaseValue> { BaseValue = value };
        }
    }

    #region Tag Attribute

    /// <summary>Interface to be followed by any attribute</summary>
    internal abstract class TagAttribute
    {
        /// <summary>Default constructor saving name of the attribute</summary>
        /// <param name="name">Name of the attribute</param>
        protected TagAttribute(string name)
        {
            Name = name;
        }

        /// <summary>Name of the attribute</summary>
        public virtual string Name { get; protected set; }

        /// <summary>Renders the value of the attribute to the output stream</summary>
        /// <param name="writer">Output stream</param>
        public abstract void Render(TextWriter writer);
    }

    /// <summary>Base attribute class.</summary>
    /// <remarks>Can store name and value of attribute in textual format. 
    /// Also con build HTML piece basing on the attribute state.</remarks>
    internal abstract class BaseAttribute<TAttributeValue, TValue> : TagAttribute
        where TAttributeValue : AttributeValue<TValue>
    {
        /// <summary>Default constructor saving name of the attribute</summary>
        /// <param name="name">Name of the attribute</param>
        protected BaseAttribute(string name) : base(name)
        {
            AttributeValue = default(TAttributeValue);
        }

        /// <summary>Value of attribute</summary>
        public virtual TAttributeValue AttributeValue { get; set; }

        /// <summary>Takes part imn HTML generation</summary>
        /// <param name="writer">Destionation of rendering</param>
        public override void Render(TextWriter writer)
        {
            writer.Write("{0}=\"{1}\"", Name, AttributeValue);
        }

        /// <summary>Conversion from attribute value instance into attribute value</summary>
        /// <param name="attribute">Attribute value instance</param>
        /// <returns>Attribute value</returns>
        public static implicit operator TAttributeValue(BaseAttribute<TAttributeValue, TValue> attribute)
        {
            return (attribute == null ? null : attribute.AttributeValue);
        }
    }

    #endregion

    #region Base attribute with conversion operators

    /// <summary>Class working with value-typed attribute base type</summary>
    /// <typeparam name="TAttributeValue">Attribute value</typeparam>
    /// <typeparam name="TValue">Base type</typeparam>
    internal abstract class BaseStructAttribute<TAttributeValue, TValue> : BaseAttribute<TAttributeValue, TValue>
        where TAttributeValue : AttributeValue<TValue>
        where TValue : struct
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected BaseStructAttribute(string name) : base(name) { }

        /// <summary>Conversion from base type of attribute value into attribute value instance</summary>
        /// <param name="attribute">Base type of attribute value</param>
        /// <returns>Attribute value instance</returns>
        public static implicit operator TValue?(BaseStructAttribute<TAttributeValue, TValue> attribute)
        {
            if (attribute == null) return null;
            return attribute.AttributeValue.BaseValue;
        }
    }

    /// <summary>Class working with reference-typed attribute base type</summary>
    /// <typeparam name="TAttributeValue">Attribute value</typeparam>
    /// <typeparam name="TValue">Base type</typeparam>
    internal abstract class BaseClassAttribute<TAttributeValue, TValue> : BaseAttribute<TAttributeValue, TValue>
        where TAttributeValue : AttributeValue<TValue>
        where TValue : class
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected BaseClassAttribute(string name) : base(name) { }

        /// <summary>Conversion from base type of attribute value into attribute value instance</summary>
        /// <param name="attribute">Base type of attribute value</param>
        /// <returns>Attribute value instance</returns>
        public static implicit operator TValue(BaseClassAttribute<TAttributeValue, TValue> attribute)
        {
            if (attribute == null) return null;
            return attribute.AttributeValue.BaseValue;
        }
    }

    #endregion

    #region Predefined types of attributes

    internal abstract class EnumBasedAttribute<TEnum> : BaseStructAttribute<AttributeValue<TEnum>, TEnum> where TEnum : struct 
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected EnumBasedAttribute(string name) : base(name) { }
    }

    internal abstract class BooleanBasedAttribute : BaseStructAttribute<AttributeValue<bool>, bool>
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected BooleanBasedAttribute(string name) : base(name) { }
    }

    internal abstract class IntegerBasedAttribute : BaseStructAttribute<AttributeValue<int>, int>
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected IntegerBasedAttribute(string name) : base(name) { }
    }

    internal abstract class CharacterBasedAttribute : BaseStructAttribute<AttributeValue<char>, char>
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected CharacterBasedAttribute(string name) : base(name) { }
    }

    internal abstract class StringBasedAttribute : BaseClassAttribute<AttributeValue<string>, string>
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected StringBasedAttribute(string name) : base(name) { }
    }

    internal abstract class DateTimeBasedAttribute : BaseStructAttribute<DateTimeValue, DateTime>
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected DateTimeBasedAttribute(string name) : base(name) { }
    }

    #endregion

    #region Special attribute types

    /// <summary>Derivated attribute class representing JavaScript events attached to the tag</summary>
    internal abstract class ScriptAttribute : StringBasedAttribute
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected ScriptAttribute(string name) : base(name) { }
    }

    /// <summary>Derivated attribute class representing inline CSS styles attached to the tag</summary>
    internal abstract class StyleSheetAttribute : StringBasedAttribute
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        protected StyleSheetAttribute(string name) : base(name) { }
    }

    internal class CustomAttribute : StringBasedAttribute
    {
        /// <summary>Default constructor forwarding to base constructor</summary>
        /// <param name="name">Name of the attribute</param>
        internal CustomAttribute(string name) : base(name) { }
    }

    #endregion
}